' Copyright (c) Microsoft Corporation.  All rights reserved.
Imports System
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.EntityClient
Imports System.Data.Metadata.Edm
Imports System.Linq
Imports System.Web.UI.WebControls

Namespace EdmMetadataHelpersVB
    ''' <summary>
    ''' A class that Wraps a runtime created Entity Framework.
    ''' </summary>
    Public Class EdmWrapper

#Region "Constructor"
        Public Sub New(ByVal name As String, ByVal provider As String, ByVal providerConnectionString As String, ByVal csdlPath As String, ByVal ssdlPath As String, ByVal mslPath As String)
            Me._name = name
            Me._provider = provider
            Me._providerConnectionString = providerConnectionString
            Me._csdlPath = csdlPath
            Me._ssdlPath = ssdlPath
            Me._mslPath = mslPath
        End Sub
#End Region

#Region "Public Methods"
        ''' <summary>
        ''' A utility function for executing the eSQL against the current EDM
        ''' and converting the resulting into a DataTable
        ''' </summary>
        Public Function GetDataTable(ByVal eSQL As String) As DataTable
            Dim table As DataTable

            Using conn As EntityConnection = New EntityConnection(Me.EdmConnectionString)
                conn.Open()
                Using cmd As EntityCommand = conn.CreateCommand
                    cmd.CommandText = eSQL
                    Using reader As EntityDataReader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)
                        table = EdmDataReaderAdapter.LoadTable(reader)
                    End Using
                End Using
            End Using

            Return table
        End Function
        ''' <summary>
        ''' A function for getting a Tree representation of the Edm
        ''' </summary>
        Public Function GetRootNode() As TreeNode
            Dim node As New TreeNode(Me.Name)
            node.ImageUrl = "assets\schema.jpg"
            node.Target = "Model"
            node.SelectAction = TreeNodeSelectAction.Select
            Dim children = From entitycontainer In Csdl.Containers _
                           Select GetNode(entitycontainer)
            For Each child In children
                node.ChildNodes.Add(child)
            Next
            Return node
        End Function

        ''' <summary>
        ''' A utility function for executing the eSQL against the current EDM 
        ''' and converting the results to an Enumeration of XElement items
        ''' </summary>
        Public Function GetElements(ByVal eSQL As String) As IEnumerable(Of XElement)
            Using conn As EntityConnection = New EntityConnection(Me.EdmConnectionString)
                conn.Open()
                Using cmd As EntityCommand = conn.CreateCommand
                    cmd.CommandText = eSQL
                    Using reader As EntityDataReader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)
                        Return EdmXmlAdapter.GetElements(reader).ToList()
                    End Using
                End Using
            End Using
        End Function
        ''' <summary>
        ''' A simple utility function for creating an EntityConnection ConnectionString
        ''' </summary>
        ''' <param name="provider">The fullname of the database provider class</param>
        ''' <param name="providerConnectionString">A provider specific connection string </param>
        ''' <param name="csdlPath">the path to the CSDL file</param>
        ''' <param name="ssdlPath">the path to the SSDL file</param>
        ''' <param name="mslPath">the path to the MSL file</param>
        Public Shared Function GetEdmConnectionString(ByVal provider As String, ByVal providerConnectionString As String, ByVal csdlPath As String, ByVal ssdlPath As String, ByVal mslPath As String) As String
            Dim builder As New EntityConnectionStringBuilder()

            builder.Metadata = String.Format("{0}{1}{2}", csdlPath, ssdlPath, mslPath)
            builder.Provider = provider
            builder.ProviderConnectionString = providerConnectionString

            Return builder.ConnectionString
        End Function
        ''' <summary>
        ''' A way to convert a RelationshipMultiplicity into a string
        ''' </summary>
        Public Shared Function GetCardinalityAsString(ByVal multiplicity As RelationshipMultiplicity) As String
            If (multiplicity = RelationshipMultiplicity.Many) Then
                Return "0-*"
            End If
            If (multiplicity = RelationshipMultiplicity.ZeroOrOne) Then
                Return "0-1"
            End If
            Return "1"
        End Function
#End Region

#Region "Node Factory"
        Protected Function GetNode(ByVal association As AssociationType) As TreeNode
            Dim node As New TreeNode(association.FullName)
            node.ImageUrl = "assets\associationtype.jpg"
            node.SelectAction = TreeNodeSelectAction.Select
            node.Target = "AssociationType"
            Return node
        End Function

        Protected Function GetNode(ByVal [property] As EdmProperty) As TreeNode
            Dim node As New TreeNode([property].Name)
            If [property].IsEntityKey() Then
                node.ImageUrl = "assets\key.jpg"
            Else
                node.ImageUrl = "assets\property.jpg"
            End If
            node.SelectAction = TreeNodeSelectAction.Select
            node.Target = "EdmProperty"
            node.ChildNodes.Add(New TreeNode(String.Format("Type='{0}'", [property].TypeUsage.EdmType.FullName)))
            Dim facet As Facet
            For Each facet In [property].TypeUsage.Facets
                node.ChildNodes.Add(Me.GetNode(facet))
            Next
            Return node
        End Function

        Protected Function GetNode(ByVal entityContainer As EntityContainer) As TreeNode

            Dim node As New TreeNode(entityContainer.Name)
            node.Target = "EntityContainer"
            node.ImageUrl = "assets\container.jpg"
            node.SelectAction = TreeNodeSelectAction.Expand

            Dim children = From es In entityContainer.BaseEntitySets.OfType(Of EntitySet)() _
                           Select GetNode(es)

            For Each child In children
                node.ChildNodes.Add(child)
            Next

            Return node

        End Function

        Protected Function GetNode(ByVal entitySetBase As EntitySetBase) As TreeNode
            Dim IsEntitySet As Boolean = TypeOf entitySetBase Is EntitySet
            Dim containedType As String = entitySetBase.ElementType.Name
            Dim node As New TreeNode(entitySetBase.Name)

            node.ToolTip = String.Format("This is an EntitySet of type '{0}'", containedType)
            node.SelectAction = TreeNodeSelectAction.SelectExpand
            node.ImageUrl = IIf(IsEntitySet, "assets\entityset.jpg", "assets\associationset.jpg")
            node.Target = IIf(IsEntitySet, "EntitySet", "AssociationSet")
            node.ChildNodes.Add(Me.GetNode(entitySetBase.ElementType))
            Return node
        End Function

        Protected Function GetNode(ByVal entityBase As EntityTypeBase) As TreeNode
            Dim node As New TreeNode(entityBase.FullName)
            node.ImageUrl = "assets\entitytype.jpg"
            node.SelectAction = TreeNodeSelectAction.SelectExpand
            node.Target = "EntityType"
            Dim et As EntityType = TryCast(entityBase, EntityType)
            If (et IsNot Nothing) Then

            End If
            Return node
        End Function

        Protected Function GetNode(ByVal facet As Facet) As TreeNode
            If (facet.Value Is Nothing) Then
                Return New TreeNode(String.Format("{0} is not set", facet.Name))
            End If
            Return New TreeNode(String.Format("{0}='{1}'", facet.Name, facet.Value.ToString))
        End Function

        Protected Function GetNode(ByVal navigationProperty As NavigationProperty) As TreeNode
            Dim node As New TreeNode(String.Format("{0} => {1} {2}", navigationProperty.Name, EdmWrapper.GetCardinalityAsString(navigationProperty.ToEndMember.RelationshipMultiplicity), navigationProperty.ToEndMember.Name))
            node.ImageUrl = "assets\relationship.jpg"
            node.SelectAction = TreeNodeSelectAction.Select
            node.Target = "NavigationProperty"
            node.ChildNodes.Add(Me.AddPrefix("From: ", Me.GetNode(navigationProperty.FromEndMember)))
            node.ChildNodes.Add(Me.AddPrefix("To: ", Me.GetNode(navigationProperty.ToEndMember)))
            Return node
        End Function

        Protected Function GetNode(ByVal relationshipEndMember As RelationshipEndMember) As TreeNode
            Return New TreeNode(String.Format("{0} ({1})", relationshipEndMember.Name, EdmWrapper.GetCardinalityAsString(relationshipEndMember.RelationshipMultiplicity)))
        End Function


        Protected Function AddPrefix(ByVal prefix As String, ByVal node As TreeNode) As TreeNode
            node.Text = (prefix & node.Text)
            Return node
        End Function
#End Region

#Region "Properties"
        Public ReadOnly Property Csdl() As EdmCsdlCatalog
            Get
                If (Me._csdl Is Nothing) Then
                    Me._csdl = New EdmCsdlCatalog(Me._csdlPath)
                End If
                Return Me._csdl
            End Get
        End Property

        Public ReadOnly Property CsdlPath() As String
            Get
                Return Me._csdlPath
            End Get
        End Property

        Public ReadOnly Property EdmConnectionString() As String
            Get
                Return EdmWrapper.GetEdmConnectionString(Me.Provider, Me.ProviderConnectionString, Me.CsdlPath, Me.SsdlPath, Me.MslPath)
            End Get
        End Property

        Public ReadOnly Property MslPath() As String
            Get
                Return Me._mslPath
            End Get
        End Property

        Public ReadOnly Property Name() As String
            Get
                Return Me._name
            End Get
        End Property

        Public ReadOnly Property Provider() As String
            Get
                Return Me._provider
            End Get
        End Property

        Public ReadOnly Property ProviderConnectionString() As String
            Get
                Return Me._providerConnectionString
            End Get
        End Property

        Public ReadOnly Property SsdlPath() As String
            Get
                Return Me._ssdlPath
            End Get
        End Property
#End Region

#Region "Fields"
        Private _csdl As EdmCsdlCatalog
        Private _csdlPath As String
        Private _mslPath As String
        Private _name As String
        Private _provider As String
        Private _providerConnectionString As String
        Private _ssdlPath As String
        Public Const DEFAULT_PROVIDER As String = "System.Data.SqlClient"
        Public Const DEFAULT_PROVIDER_CONN_STR As String = "Data Source=.\SQLEXPRESS;Initial Catalog=[Database];Integrated Security=True"
        Public Const EDM_CONN_STR As String = "metadata={csdl}|{ssdl}|{msl};provider={provider};provider connection string=""{provider_connection_string}"""
#End Region

    End Class
End Namespace

